#include "stdafx.h"
#include "ShellPidl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


///////////////////////////////////////////////////////////////////////
//
// This source is part of CShellTree - Selom Ofori
//
// Version: 1.02 (any previously unversioned copies are older/inferior
//
// This code is free for all to use. Mutatilate it as much as you want
// See MFCENUM sample from microsoft

//
// FUNCTIONS THAT DEAL WITH PIDLs
//
/****************************************************************************
*
*    FUNCTION: Next(LPCITEMIDLIST pidl)
*
*    PURPOSE:  Gets the next PIDL in the list
*
****************************************************************************/
LPITEMIDLIST CShellPidl::Next(LPCITEMIDLIST pidl) {
    LPSTR lpMem = (LPSTR)pidl;
    lpMem += pidl->mkid.cb;
    return (LPITEMIDLIST)lpMem;
}

/****************************************************************************
*
*    FUNCTION: GetSize(LPCITEMIDLIST pidl)
*
*    PURPOSE:  Gets the size of the PIDL
*
****************************************************************************/
UINT CShellPidl::GetSize(LPCITEMIDLIST pidl) {
    UINT cbTotal = 0;
    if (pidl) {
        cbTotal += sizeof(pidl->mkid.cb);       // Null terminator
        while (pidl->mkid.cb) {
            cbTotal += pidl->mkid.cb;
            pidl = Next(pidl);
        }
    }
    return cbTotal;
}

/****************************************************************************
*
*    FUNCTION: CreatePidl(UINT cbSize)
*
*    PURPOSE:  Allocates a PIDL
*
****************************************************************************/
LPITEMIDLIST CShellPidl::CreatePidl(UINT cbSize) {
    LPMALLOC lpMalloc;
    HRESULT  hr;
    LPITEMIDLIST pidl = NULL;
    hr = SHGetMalloc(&lpMalloc);
    if (FAILED(hr))
        return 0;
    pidl = (LPITEMIDLIST)lpMalloc->Alloc(cbSize);
    if (pidl)
        memset(pidl, 0, cbSize);      // zero-init for external task   alloc
    if (lpMalloc) lpMalloc->Release();
    return pidl;
}

/****************************************************************************
*
*    FUNCTION: ConcatPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
*
*    PURPOSE:  Concatenates two PIDLs
*
****************************************************************************/
LPITEMIDLIST CShellPidl::ConcatPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) {
    LPITEMIDLIST pidlNew;
    UINT cb1;
    UINT cb2;
    if (pidl1)  //May be NULL
        cb1 = GetSize(pidl1) - sizeof(pidl1->mkid.cb);
    else
        cb1 = 0;
    cb2 = GetSize(pidl2);
    pidlNew = CreatePidl(cb1 + cb2);
    if (pidlNew) {
        if (pidl1)
            memcpy(pidlNew, pidl1, cb1);
        memcpy(((LPSTR)pidlNew) + cb1, pidl2, cb2);
    }
    return pidlNew;
}

/****************************************************************************
*
*    FUNCTION: CopyITEMID(LPMALLOC lpMalloc, LPITEMIDLIST lpi)
*
*    PURPOSE:  Copies the ITEMID
*
****************************************************************************/
LPITEMIDLIST CShellPidl::CopyITEMID(LPMALLOC lpMalloc, LPITEMIDLIST lpi) {
    LPITEMIDLIST lpiTemp;
    lpiTemp = (LPITEMIDLIST)lpMalloc->Alloc(lpi->mkid.cb + sizeof(lpi->mkid.cb));
    CopyMemory((PVOID)lpiTemp, (CONST VOID *)lpi, lpi->mkid.cb + sizeof(lpi->mkid.cb));
    return lpiTemp;
}

/****************************************************************************
*
*    FUNCTION: GetName(LPSHELLFOLDER lpsf,LPITEMIDLIST  lpi,DWORD dwFlags,
*             LPSTR         lpFriendlyName)
*
*    PURPOSE:  Gets the friendly name for the folder
*
****************************************************************************/
BOOL CShellPidl::GetName(LPSHELLFOLDER lpsf,
                         LPITEMIDLIST  lpi,
                         DWORD         dwFlags,
                         LPSTR         lpFriendlyName) {
    BOOL   bSuccess = TRUE;
    STRRET str;
    if (NOERROR == lpsf->GetDisplayNameOf(lpi, dwFlags, &str)) {
        switch (str.uType) {
        case STRRET_WSTR:
            WideCharToMultiByte(CP_ACP,                 // CodePage
                                0,		               // dwFlags
                                str.pOleStr,            // lpWideCharStr
                                -1,                     // cchWideChar
                                lpFriendlyName,         // lpMultiByteStr
                                MAX_PATH,
                                //sizeof(lpFriendlyName), // cchMultiByte, wrong. sizeof on a pointer, psk, psk
                                NULL,                   // lpDefaultChar,
                                NULL);                  // lpUsedDefaultChar
            break;
        case STRRET_OFFSET:
            lstrcpy(lpFriendlyName, (LPSTR)lpi + str.uOffset);
            break;
        case STRRET_CSTR:
            lstrcpy(lpFriendlyName, (LPSTR)str.cStr);
            break;
        default:
            bSuccess = FALSE;
            break;
        }
    } else
        bSuccess = FALSE;
    return bSuccess;
}

/****************************************************************************
*
*  FUNCTION: DoTheMenuThing(HWND hwnd,
*                           LPSHELLFOLDER lpsfParent,
*                           LPITEMIDLIST  lpi,
*                           LPPOINT lppt)
*
*  PURPOSE: Displays a popup context menu, given a parent shell folder,
*           relative item id and screen location.
*
*  PARAMETERS:
*    hwnd       - Parent window handle
*    lpsfParent - Pointer to parent shell folder.
*    lpi        - Pointer to item id that is relative to lpsfParent
*    lppt       - Screen location of where to popup the menu.
*
*  RETURN VALUE:
*    Returns TRUE on success, FALSE on failure
*
****************************************************************************/
BOOL CShellPidl::DoTheMenuThing(HWND hwnd, LPSHELLFOLDER lpsfParent,
                                LPITEMIDLIST  lpi, LPPOINT lppt) {
    LPCONTEXTMENU lpcm;
    HRESULT       hr;
    char          szTemp[64];
    CMINVOKECOMMANDINFO cmi;
    DWORD               dwAttribs = 0;
    int                 idCmd;
    HMENU               hMenu;
    BOOL                bSuccess = TRUE;
    hr = lpsfParent->GetUIObjectOf(hwnd,
                                   1,  //Number of objects to get attributes of
                                   (const struct _ITEMIDLIST **)&lpi,
                                   IID_IContextMenu,
                                   0,
                                   (LPVOID *)&lpcm);
    if (SUCCEEDED(hr)) {
        hMenu = CreatePopupMenu();
        if (hMenu) {
            hr = lpcm->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
            if (SUCCEEDED(hr)) {
                idCmd = TrackPopupMenu(hMenu,
                                       TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,
                                       lppt->x, lppt->y, 0, hwnd, NULL);
                if (idCmd) {
                    cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
                    cmi.fMask  = 0;
                    cmi.hwnd   = hwnd;
                    cmi.lpVerb = MAKEINTRESOURCE(idCmd - 1);
                    cmi.lpParameters = NULL;
                    cmi.lpDirectory  = NULL;
                    cmi.nShow        = SW_SHOWNORMAL;
                    cmi.dwHotKey     = 0;
                    cmi.hIcon        = NULL;
                    hr = lpcm->InvokeCommand(&cmi);
                    if (!SUCCEEDED(hr))
                        wsprintf(szTemp, "InvokeCommand failed. hr=%lx", hr);
                }
            } else
                bSuccess = FALSE;
            DestroyMenu(hMenu);
        } else
            bSuccess = FALSE;
        lpcm->Release();
    } else {
        wsprintf(szTemp, "GetUIObjectOf failed! hr=%lx", hr);
        bSuccess = FALSE;
    }
    return bSuccess;
}

/****************************************************************************
*
*    FUNCTION: GetIcon(LPITEMIDLIST lpi, UINT uFlags)
*
*    PURPOSE:  Gets the index for the current icon.  Index is index into system
*              image list.
*
*  PARAMETERS:
*    lpi    - Fully qualified item id list for current item.
*    uFlags - Flags for SHGetFileInfo()
*
*  RETURN VALUE:
*    Icon index for current item.
****************************************************************************/
int CShellPidl::GetItemIcon(LPITEMIDLIST lpi, UINT uFlags) {
    SHFILEINFO    sfi;
    SHGetFileInfo((LPCSTR)lpi,
                  0,
                  &sfi,
                  sizeof(SHFILEINFO),
                  uFlags);
    return sfi.iIcon;
}
